using UnityEngine;
using System.Collections;

/// \class  gkAbyssalGiantSubmarineAnimationController
/// \brief  The submarine animation controller
[AddComponentMenu("GK/AbyssalGiant/Submarine/gkAbyssalGiantSubmarineAnimationController")]
public class gkAbyssalGiantSubmarineAnimationController : MonoBehaviour 
{
	enum ESubmarineMode
	{
		ESM_Horizontal,
		ESM_Vertical,
		ESM_Boost,
		ESM_Shock,
		
		ESM_Count
	};

	/// The minimum vertical velocity to play the vertical animation
	public float MinVerticalVelocity = 1.0f;
	
	/// The maximum vertical velocity to play the vertical animation
	public float MaxVerticalVelocity = 15.0f;
	
	/// The maximum vertical velocity to play the horizontal animation
	public float MaxHorizontalVelocity = 15.0f;
	
	/// The blend time
	public float BlendTime = 0.25f;
	
	/// The turbulence anim node
	private AnimationState m_rAnimationStateTurbulence;
	
	/// The inclination up state name
	private string m_strAnimationStatesInclinationUpName;
	
	/// The inclination down state name
	private string m_strAnimationStatesInclinationDownName;
	
	/// Mapping between animation mode and animation names
	private string[] m_oSubmarineModeAnimNames = new string[(int)ESubmarineMode.ESM_Count];
	
	/// The scrolling info
	private gkScrollingInfo m_rScrollingInfo;
	
	/// Is the owner boosting?
	private bool m_bBoosting;
	
	/// Is the owner shocked?
	private bool m_bShocked;
	
	/// The current submarine mode
	private ESubmarineMode m_eCurrentSubmarineMode = ESubmarineMode.ESM_Vertical;
	
	/// The physic component
	private gkPhysicController m_rPhysicController;

	/// \brief  Called just after the game begin to play
	void Start()
	{
		// Get the physic component
		m_rPhysicController = GetComponent<gkPhysicController>();
	
		// Get the turbulence animation
		m_rAnimationStateTurbulence = animation["Submarine_Turbulence"];
		
		// Setup the inclination states
		m_strAnimationStatesInclinationUpName = "Submarine_Up";
		m_strAnimationStatesInclinationDownName = "Submarine_Down";
		
		// Setup the animation mode states names
		m_oSubmarineModeAnimNames[(int)ESubmarineMode.ESM_Horizontal] = "Submarine_Turbulence";
		m_oSubmarineModeAnimNames[(int)ESubmarineMode.ESM_Boost] = "Submarine_Boost";
		m_oSubmarineModeAnimNames[(int)ESubmarineMode.ESM_Shock] = "Submarine_Shock";
		
		// Put the effect animation on a other layer
		animation["Submarine_PickUp"].layer = 1;
	}
	
	/// \brief  Called when the component is enabled
	void OnEnable() 
	{
		// Grab the scrolling info
	    m_rScrollingInfo = GameObject.Find("ScrollingInfo").GetComponent<gkScrollingInfo>();
		
		// Listen the boost event of the scrolling info
		m_rScrollingInfo.GetComponent<gkEventComponent>().AddEventHandler(gkBoostEvent.BoostBegin, HandleBoostBeginEvent);
		m_rScrollingInfo.GetComponent<gkEventComponent>().AddEventHandler(gkBoostEvent.BoostEnd, HandleBoostEndEvent);
		// Listen the shock event of the owner
		GetComponent<gkEventComponent>().AddEventHandler(gkAbyssalGiantShockEvent.ShockBegin, HandleShockBeginEvent);
		GetComponent<gkEventComponent>().AddEventHandler(gkAbyssalGiantShockEvent.ShockEnd, HandleShockEndEvent);
	}
	
	/// \brief  Called when the component is disabled
	void OnDisable() 
	{	
		// Remove the boost event of the scrolling info
		if(m_rScrollingInfo != null)
		{
			m_rScrollingInfo.GetComponent<gkEventComponent>().RemoveEventHandler(gkBoostEvent.BoostBegin, HandleBoostBeginEvent);
			m_rScrollingInfo.GetComponent<gkEventComponent>().RemoveEventHandler(gkBoostEvent.BoostEnd, HandleBoostEndEvent);
		}
		// Remove the shock event of the owner
		GetComponent<gkEventComponent>().RemoveEventHandler(gkAbyssalGiantShockEvent.ShockBegin, HandleShockBeginEvent);
		GetComponent<gkEventComponent>().RemoveEventHandler(gkAbyssalGiantShockEvent.ShockEnd, HandleShockEndEvent);
	}
	
	/// \brief  Called each frame
	void Update()
	{
		ESubmarineMode eSubmarineModeCurrent;
		float fHorizontalVelocity;
		float fVerticalVelocity;
		float fVerticalVelocityAbs;
	
		// Select the correct anim
		if(m_bShocked)
		{
			eSubmarineModeCurrent = ESubmarineMode.ESM_Shock;
		}
		else if(m_bBoosting)
		{
			eSubmarineModeCurrent = ESubmarineMode.ESM_Boost;
		}			
		else
		{
			fVerticalVelocity = m_rPhysicController.GetLinearVelocity().y;
			fVerticalVelocityAbs = Mathf.Abs(fVerticalVelocity);
	
			// Is the movement horizontal ?
			if(fVerticalVelocityAbs  < MinVerticalVelocity)
			{
				eSubmarineModeCurrent = ESubmarineMode.ESM_Horizontal;
				
				// Adapt the turbulence rate at the horizontal velocity
				fHorizontalVelocity = m_rPhysicController.GetLinearVelocity().x;
				m_rAnimationStateTurbulence.speed = (fHorizontalVelocity + MaxHorizontalVelocity) / (MaxHorizontalVelocity * 2);
			}
			else
			{
				eSubmarineModeCurrent = ESubmarineMode.ESM_Vertical;
	
				// Adapt the inclination percent at the vertical velocity
				SetSubmarineInclination((fVerticalVelocity + MaxVerticalVelocity) / (MaxVerticalVelocity * 2));
			}
		}
		SetSubmarineMode(eSubmarineModeCurrent);
	}
	
	/// \brief  Set the submarine mode
	void SetSubmarineMode(ESubmarineMode a_eSubmarineMode, bool a_bReplay = false)
	{
		if(a_bReplay || a_eSubmarineMode != m_eCurrentSubmarineMode)
		{	
			// If we are in a vertical mode activate the two animation states used for blending 
			if(a_eSubmarineMode == ESubmarineMode.ESM_Vertical)
			{
				// Blend the current animation to zero
				animation.Blend(m_oSubmarineModeAnimNames[(int)m_eCurrentSubmarineMode], 0.0f, BlendTime);
			}
			else
			{	
				// Else cross fade to the animation state corresponding to the mode
				animation.CrossFade(m_oSubmarineModeAnimNames[(int)a_eSubmarineMode], BlendTime);
			}
			m_eCurrentSubmarineMode = a_eSubmarineMode;
		}
	}
	
	/// \brief  Set the submarine inclination
	/// \param[in]  a_fInclinationPercent 	1->0 = Down->Up
	void SetSubmarineInclination(float a_fInclinationPercent)
	{
		animation.Blend(m_strAnimationStatesInclinationDownName, 1.0f - a_fInclinationPercent, BlendTime);
		animation.Blend(m_strAnimationStatesInclinationUpName, a_fInclinationPercent, BlendTime);
	}
	
	/// Handle the boost event
	void HandleBoostBeginEvent(Object a_rEvent)
	{
		if(m_rScrollingInfo.IsAccelerated())
		{
			SetBoosting(true);
			SetSubmarineMode(ESubmarineMode.ESM_Boost);
		}
		else
		{
			SetBoosting(false);
		}
	}

	/// \brief  Handle the boost event
	void HandleBoostEndEvent(Object a_rEvent)
	{
		SetBoosting(false);
	}
	
	/// \brief  Set if the submarine is boosting or not
	void SetBoosting(bool a_bBoosting)
	{
		if(m_bBoosting != a_bBoosting)
		{
			m_bBoosting = a_bBoosting;
		}
	}
	
	/// \brief  Handle the owner collision event
	void HandleShockBeginEvent(Object a_rEvent)
	{
		SetShocked(true);
		SetSubmarineMode(ESubmarineMode.ESM_Shock, true);
	}
	
	/// \brief  Handle the owner collision event
	void HandleShockEndEvent(Object a_rEvent)
	{
		SetShocked(false);
	}
	
	/// \brief  Set if the submarine is boosting or not
	void SetShocked(bool a_bShocked)
	{
		if(m_bShocked != a_bShocked)
		{
			m_bShocked = a_bShocked;
		}
	}
}